package Meter::Utils;

#===============================================================================
#     REVISION:  $Id: Utils.pm 141 2011-07-26 15:05:28Z xdr.box@gmail.com $
#  DESCRIPTION:  Helper methods
#===============================================================================

use strict;
use warnings;

use Readonly;
Readonly our $VERSION => qw($Revision: 141 $) [1];

use English qw( -no_match_vars );
use Data::Dumper;
use DateTime;
use DateTime::Format::ISO8601;
use List::Util qw( reduce );

#use Smart::Comments;

use Meter::Log;

use base qw(Exporter);

our @EXPORT_OK = qw(
    dump_var
    dump_compact
    check_METER_ROOT
    get_action_module_by_action_name
    get_action_name_by_action_module
    generate_session_id
    check_session_id
    inflate_iso8601_datetime
    deflate_iso8601_datetime
    inflate_iso8601_date
    deflate_iso8601_date
    get_webapp_url
    get_site_url
    get_most_recent_user_metering
    get_number_of_registered_users
    get_number_of_users_online
    get_number_of_stored_meterings
);

Readonly my $MAX_ACTION_NAME_LENGTH => 32;
Readonly my $SESSION_ID_LENGTH      => 32;

## no critic (ProhibitEnumeratedClasses)

sub dump_var {
    my $var = shift;
    my $label = shift || 'VAR';

    return Data::Dumper->Dump( [$var], [$label] );
}

sub dump_compact {
    my $var = shift;
    my $label = shift || 'VAR';

    my $d = Data::Dumper->new( [$var], [$label] );
    $d->Indent(0);

    return $d->Dump();
}

sub check_METER_ROOT {    ## no critic (Capitalization)
    if ( !$ENV{'METER_ROOT'} || !-d $ENV{'METER_ROOT'} ) {
        die "Please set METER_ROOT environment variable\n";
    }

    return;
}

sub get_action_module_by_action_name {
    my $action_name = shift;

    return if !$action_name;
    return if ( length($action_name) > $MAX_ACTION_NAME_LENGTH );
    return if $action_name !~ /\A[a-z0-9_]+\z/xms;

    $action_name =~ s/\A([a-z])/uc($1)/exms;
    $action_name =~ s/_([a-z])/uc($1)/gexms;

    if ( -e "$ENV{'METER_ROOT'}/lib/Meter/Action/$action_name.pm" ) {
        return "Meter::Action::$action_name";
    }
    else {
        return;
    }
}

sub get_action_name_by_action_module {
    my $action_module = shift;

    return if !$action_module;

    $action_module =~ s/\A.*:://xms;
    $action_module =~ s/(?<=[a-z0-9])([A-Z])/lc("_$1")/gexms;

    return lc $action_module;
}

sub generate_session_id {
    ## no critic (ProhibitMagicNumbers)
    my @alpha = ( 'A' .. 'Z', 'a' .. 'z', 0 .. 9 );

    return join q{},
        map { $alpha[ int rand @alpha ] } 1 .. $SESSION_ID_LENGTH;
}

sub check_session_id {
    my $session_id = shift;

    return 0 if !$session_id;
    return 0 if $session_id !~ qr/\A[A-Za-z0-9]{$SESSION_ID_LENGTH}\z/xms;

    return 1;
}

sub inflate_iso8601_datetime {
    my $str = shift;

    return DateTime::Format::ISO8601->parse_datetime($str);
}

sub deflate_iso8601_datetime {
    my $dt = shift;

    return $dt->strftime('%Y%m%dT%H%M%S%z');
}

sub inflate_iso8601_date {
    my $str = shift;

    return inflate_iso8601_datetime($str);
}

sub deflate_iso8601_date {
    my $dt = shift;

    return $dt->strftime('%Y%m%d');
}

sub get_webapp_url {
    my $action = shift;

    my $url
        = 'http://'
        . $ENV{'SERVER_NAME'}
        . ( $ENV{'SERVER_PORT'} eq '80' ? q{} : ":$ENV{'SERVER_PORT'}" )
        . $ENV{'SCRIPT_NAME'}
        . ( $action ? "?action=$action" : q{} );

    return $url;
}

sub get_site_url {
    my $action = shift;

    my $url
        = 'http://'
        . $ENV{'SERVER_NAME'}
        . ( $ENV{'SERVER_PORT'} eq '80' ? q{} : ":$ENV{'SERVER_PORT'}" );

    return $url;
}

sub get_most_recent_user_metering {
    my $user = shift;

    my $logger     = get_logger();
    my $username   = $user->username();
    my $appartment = $user->appartment();

    if ( !$appartment ) {
        $logger->warn("User '$username' has not entered appartment info yet");
        return;
    }

    my @meterings = $appartment->meterings();

    if ( !@meterings ) {
        $logger->debug("User '$username' has no meterings entered yet");
        return;
    }

    my $most_recent
        = reduce { $a->start_date() > $b->start_date() ? $a : $b } @meterings;

    $logger->debug(
              "Most recent metering of user '$username' has start_date: "
            . $most_recent->start_date()
            . ' end_date: '
            . $most_recent->end_date() );

    return $most_recent;
}

sub get_number_of_registered_users {
    return scalar Meter::Users->retrieve_all();
}

sub get_number_of_users_online {
    my $idle_online_minutes_at_most = shift;
    my $time_zone                   = shift;

    my $now = DateTime->now( time_zone => $time_zone );
    my $min_idle_time
        = $now->clone()->subtract( minutes => $idle_online_minutes_at_most );

    my @active_sessions
        = grep { $_->last_activity_datetime() >= $min_idle_time }
        Meter::Sessions->retrieve_all();

    my %unique_users = ();
    foreach my $active_session (@active_sessions) {
        $unique_users{ $active_session->user()->user() } = 1;
    }

    return scalar keys %unique_users;
}

sub get_number_of_stored_meterings {
    return scalar Meter::Meterings->retrieve_all();
}

1;
